package jdklearning.concurrent.thread;


import java.util.Random;


/**
 *  

为了分析DCL，我需要预先陈述上面程序运行时几个事实：

语句(5)只会被执行一次，也就是LazySingleton只会存在一个实例，这是由于它和语句(4)被放在同步块中被执行的缘故，如果去掉语句(3)处的同步块，那么这个假设便不成立了。
instance只有两种“曾经可能存在”的值，要么为null，也就是初始值，要么为执行语句(5)时构造的对象引用。这个结论由事实1很容易推出来。
getInstance()总是返回非空值，并且每次调用返回相同的引用。如果getInstance()是初次调用，它会执行语句(5)构造一个LazySingleton实例并返回，如果getInstance()不是初次调用，如果不能在语句(2)处检测到非空值，那么必定将在语句(4)处就能检测到instance的非空值，因为语句(4)处于同步块中，对instance的写入--语句(5)也处于同一个同步块中。
有读者可能要问了，既然根据第３条事实getInstance()总是返回相同的正确的引用，为什么还说DCL有问题呢？
这里的关键是 尽管得到了LazySingleton的正确引用，但是却有可能访问到其成员变量 的 不正确值 ，具体来说LazySingleton.getInstance().getSomeField()有可能返回someField的默认值0。
如果程序行为正确的话，这应当是不可能发生的事，因为在构造函数里设置的someField的值不可能为0。
为也说明这种情况理论上有可能发生，我们只需要说明语句(1)和语句(7)并不存在happen-before关系。
 */
public class DCLTest {

	private int someField;  
    
    private static DCLTest instance;  
      
    private DCLTest() {  
        this.someField = new Random().nextInt(200)+1;         // (1)  
    }  
      
    public static DCLTest getInstance() {  
        if (instance == null) {                               // (2)  
            synchronized(DCLTest.class) {               // (3)  
                if (instance == null) {                       // (4)  
                    instance = new DCLTest();           // (5)  
                }  
            }  
        }  
        return instance;                                      // (6)  
    }  
      
    public int getSomeField() {  
        return this.someField;                                // (7)  
    }  
    
    
    public static void main(String[] args) {
		for(int i=0;i<10;i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					System.out.println(DCLTest.getInstance().getSomeField());
				}
			}).start();
		}
	}
}
